﻿using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using App_Code;
using MapSurface.LaoMap;

public partial class MapTile : System.Web.UI.Page
{
    const string CACHE = "cache";
    string _cacheRoot = null;
    
    static readonly Random _randObj = new Random(0);
    // The important thing... WPF wants a STA thread, and one with a message pump.
    // this explains why... to some extent.
    // http://www.eggheadcafe.com/software/aspnet/29864429/disposable-canvas.aspx

    protected void Page_Load(object sender, EventArgs e)
    {
        IconRepository.TemplateLoader(this);
        try
        {
            Response.Expires = -1;

            int zoom = int.Parse(Request.QueryString["zoom"]);
            int x = int.Parse(Request.QueryString["x"]);
            int y = int.Parse(Request.QueryString["y"]);

            string layerType = Request.QueryString["layer"];

            double zoomFactor = Math.Pow(2, zoom);
            double lon = (x / zoomFactor) * 360.0 - 180.0;
            double tileEast = ((x + 1) / zoomFactor) * 360.0 - 180.0;

            // http://wiki.openstreetmap.org/wiki/Mercator
            double y1 = (0.5 - (y / zoomFactor)) * 360;
            double lat = 180 / Math.PI * (2 * Math.Atan(Math.Exp(y1 * Math.PI / 180)) - Math.PI / 2);

            double y1Base = (0.5 - ((y + 1) / zoomFactor)) * 360;
            double latTileBase = 180 / Math.PI * (2 * Math.Atan(Math.Exp(y1Base * Math.PI / 180)) - Math.PI / 2);

            //_startEvent.Set();
            //  Response.AppendHeader("pragma", "no-store,no-cache"); // //HTTP 1.0
            // Response.AppendHeader("cache-control", "no-cache, no-store,must-revalidate, max-age=-1"); // HTTP 1.1
            // Response.AppendHeader("expires", "-1");
            Response.ContentType = "image/png";

            using (var io = new MemoryStream())
            {
                DoIt(io, lat, lon, zoomFactor, latTileBase, tileEast, layerType);
                Response.BinaryWrite(io.GetBuffer());
                io.Close();
            }
        }
        catch (Exception ex)
        {
            SaveError(ex.Message);
        }
    }

    private void SaveError(string p)
    {
        try
        {
            using (TextWriter newWriter = new StreamWriter("C:\\osm\\error.txt", true))
            {
                newWriter.WriteLine(p);
                newWriter.Flush();
                newWriter.Close();
            }
        }
        catch (Exception)
        {
        }
    }

    private void DoIt(MemoryStream io, double lat, double lon, double scale, double latTileBase, double tileEast, string layerType)
    {
        const int width = 256;
        const int height = 256;

        if (!MapTiler.IsTileInLaos(lat, lon, latTileBase, tileEast))
        {
            // spoof up an empty png

            var bmp1 = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);

            BitmapFrame bmf = BitmapFrame.Create(bmp1);
            bmf.Freeze();

            // choose the format if you want something other than png
            var png = new PngBitmapEncoder();

            png.Frames.Add(bmf);

            png.Save(io);
            io.Seek(0, SeekOrigin.Begin);
            return;
        }

        string filename;
        if (IsCached(out filename, layerType))
        {
            var data = File.ReadAllBytes(filename);
            io.Write(data, 0, data.Length);
            return;
        }

        var availableSize = new Size(width, height);

       //  Canvas canvas = GetTestCanvas(availableSize, lat, lon);

        EnumMapLayer enumMapLayer = EnumMapLayer.Background;

        switch (layerType)
        {
            case "traffic": enumMapLayer = EnumMapLayer.Traffic;
                break;
            case "border": enumMapLayer = EnumMapLayer.Border;
                break;
        }

        Canvas canvas = GetMapCanvas(availableSize, lat, lon, scale, latTileBase, enumMapLayer);

        canvas.Measure(availableSize);
        canvas.Arrange(new Rect(availableSize));

        //http://www.codeproject.com/KB/WPF/BitmapToBitmapSource.aspx?display=PrintAll

        // google for ChainedBitmapSourcesExample - working with bitmap images

        {
            var bmp1 = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
            bmp1.Render(canvas);

            {
                BitmapFrame bmf = BitmapFrame.Create(bmp1);
                bmf.Freeze();

                {
                    PngBitmapEncoder png = new PngBitmapEncoder();
                    png.Frames.Add(bmf);
                    png.Save(io);
                    io.Seek(0, SeekOrigin.Begin);
                    SaveFileInCacheLocation1(io, layerType);
                    io.Seek(0, SeekOrigin.Begin);
                }
            }

            canvas.Children.Clear(); // removing the children from the canvas has a big impact of garbage collection
        }
    }

    private void SaveFileInCache(PngBitmapEncoder png, string layerType)
    {
        SaveFileInCacheLocation(AutoGetCacheRoot(), png, layerType);
    }

    private string AutoGetCacheRoot()
    {
        if (_cacheRoot != null)
            return _cacheRoot;

        if (File.Exists(@"c:\osm\osm.bin"))
        {
            _cacheRoot = @"c:\osm\";
            return _cacheRoot;
        }
        _cacheRoot =  @"f:\osm\";
        return _cacheRoot;
    }

    private bool IsCached(out string fileName, string mapLayer)
    {
        int zoom = int.Parse(Request.QueryString["zoom"]);
        int x = int.Parse(Request.QueryString["x"]);
        int y = int.Parse(Request.QueryString["y"]);
        fileName = AutoGetCacheRoot() + CACHE + "\\" + mapLayer + "\\" + zoom + "\\" + x + "\\" + y + ".png";

        //return false;
        return File.Exists(fileName);
    }


    private void SaveFileInCacheLocation(string path, PngBitmapEncoder png, string layerType)
    {
        int zoom = int.Parse(Request.QueryString["zoom"]);
        int x = int.Parse(Request.QueryString["x"]);
        int y = int.Parse(Request.QueryString["y"]);

        if (!Directory.Exists(path + CACHE))
        {
            Directory.CreateDirectory(path + CACHE);
        }

        if (!Directory.Exists(path + CACHE + "\\" + layerType))
        {
            Directory.CreateDirectory(path + CACHE + "\\" + layerType);
        }

        if (!Directory.Exists(path + CACHE + "\\" + layerType + "\\" + zoom))
        {
            Directory.CreateDirectory(path + CACHE + "\\" + layerType + "\\" + zoom);
        }

        if (!Directory.Exists(path + CACHE + "\\" + layerType + "\\" + zoom + "\\" + x))
        {
            Directory.CreateDirectory(path + CACHE + "\\" + layerType + "\\" + zoom + "\\" + x);
        }

        using (var fs = File.Create(path + CACHE + "\\" + layerType + "\\" + zoom + "\\" + x + "\\" + y + ".png"))
        {
            png.Save(fs);
            fs.Close();
        }
    }


    private void SaveFileInCacheLocation1(MemoryStream io, string layerType)
    {
        string path = AutoGetCacheRoot();
        int zoom = int.Parse(Request.QueryString["zoom"]);
        int x = int.Parse(Request.QueryString["x"]);
        int y = int.Parse(Request.QueryString["y"]);

        if (!Directory.Exists(path + CACHE))
        {
            Directory.CreateDirectory(path + CACHE);
        }

        if (!Directory.Exists(path + CACHE + "\\" + layerType))
        {
            Directory.CreateDirectory(path + CACHE + "\\" + layerType);
        }

        if (!Directory.Exists(path + CACHE + "\\" + layerType + "\\" + zoom))
        {
            Directory.CreateDirectory(path + CACHE + "\\" + layerType + "\\" + zoom);
        }

        if (!Directory.Exists(path + CACHE + "\\" + layerType + "\\" + zoom + "\\" + x))
        {
            Directory.CreateDirectory(path + CACHE + "\\" + layerType + "\\" + zoom + "\\" + x);
        }

        using (var fs = File.Create(path + CACHE + "\\" + layerType + "\\" + zoom + "\\" + x + "\\" + y + ".png"))
        {
            io.WriteTo(fs);
            fs.Close();
        }
    }

    private static Canvas GetMapCanvas(Size availableSize, double lat, double lon, double scale, double latTileBase, EnumMapLayer enumMapLayer)
    {
        var canvas = new Canvas();
        MapTiler.DoOsmPlotPickerMap(canvas, availableSize, lat, lon, scale, latTileBase, enumMapLayer);
        return canvas;
    }

    private Canvas GetTestCanvas(Size availableSize, double lat, double lon)
    {
        int r = _randObj.Next(255);
        int g = _randObj.Next(255);
        int b = _randObj.Next(255);

        // Create a LinearGradientBrush and use it to
        // paint the rectangle.
        var myBrush = new LinearGradientBrush();
        myBrush.GradientStops.Add(new GradientStop(Color.FromArgb(0, 0, 0, 0), 0.0));
        myBrush.GradientStops.Add(new GradientStop(Color.FromArgb(128, 255, 200, 0), 0.5));
        myBrush.GradientStops.Add(new GradientStop(Color.FromArgb(128, (byte)r, (byte)g, (byte)b), 1.0));

        var cir = new Ellipse
                      {
                          Height = availableSize.Height,
                          Width = availableSize.Width,
                          Stroke = Brushes.Black,
                          StrokeThickness = 3.0,
                          Fill = myBrush
                      };

        var canvas = new Canvas();
        canvas.Children.Add(cir);

        var tb1 = new TextBlock { Text = " " + lat + " " + lon };

        var tb2 = new TextBlock { Text = DateTime.Now.ToLongTimeString() };

        canvas.Children.Add(tb1);
        Canvas.SetLeft(tb1, 15);
        Canvas.SetTop(tb1, 120);

        canvas.Children.Add(tb2);
        Canvas.SetLeft(tb2, 50);
        Canvas.SetTop(tb2, 140);
        return canvas;
    }
}